#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from functools import reduce
import functools
import types


def f(x):
    return x * x
# 函数式编程  高阶函数  即函数作为参数传入


def highfunc(x, y, f):
    return f(x) + f(y)


print(highfunc(10, 5, f))
# map/reduce  列表生成式无法进行太复杂的操作，但map可以执行一个函数
print("列表生成式", [x * x for x in [1, 2, 3, 4, 5, 6, 7, 8, 9]])
print("map", list(map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])))


def toInt(x, y):
    return x * 10 + y


def charToNum(s):
    return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7,
            '8': 8, '9': 9}.get(s)


print("reduce:%d" % reduce(toInt, map(charToNum, '74134021')))

# 练习1


def nFormat(name):
    nameF = ''
    if isinstance(name, str):
        # nameF = name.lower().capitalize()
        nameF = name[0].upper() + name[1:].lower()
    return nameF


print(list(map(nFormat, ['asd', 'DeAn', 'aliCe'])))


# 练习2
def prod(L):
    def prod_in(x, y):
        return x * y
    return reduce(prod_in, L)


print('3 * 5 * 7 * 9 =', prod([3, 5, 7, 9]))


# 练习3
def str2float(s):
    index = int(s.index('.'))
    return reduce(toInt, map(charToNum, s[0:index])) + reduce(
        toInt, map(charToNum, s[index + 1:])) / 10**len(str(s[index + 1:]))


print('str2float(\'123.456\') =', str2float('123.456'))


# filter 类似map，过滤list, 返回的是一个迭代器
def defaultNL():
    n = 1
    while True:
        n = n + 2
        yield n


def filterNL(n):
    return lambda x: x % n > 0  # 匿名函数  lambda表达式，暂不懂


def findSus():
    list = defaultNL()
    while True:
        n = next(list)
        yield n
        list = filter(filterNL(n), list)


i = 0
flist = findSus()
while i < 10:
    print(next(flist))
    i = i + 1


# 练习，过滤回数
def filterHs(x):
    s = str(x)
    s_r = s[::-1]  # 翻转
    if s[:len(s) // 2] == s_r[:len(s) // 2]:
        return True
    else:
        return False


print("filter:", list(filter(filterHs, list(range(1000)))))

# sorted排序 练习
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]


def by_name(stu):
    return stu[0]


def by_score(stu):
    return int(stu[1])


print("by_name", sorted(L, key=by_name))
print("by_score", sorted(L, key=by_score))

# 返回函数练习  返回函数中不能包含可变参数（循环等）


def returnFun(i):
    def g():
        return i * i
    return g


funs = []
for x in list(range(4)):
    funs.append(returnFun(x))
for x in list(range(4)):
    print(funs[x]())


# 装饰器   在调用函数前先调用装饰器
# 二层装饰器
def log(func):
    def warpper(*args, **kw):
        print("调用函数", func.__name__)
        return func(*args, **kw)
    return warpper


@log
def de():
    print("hehe")


de()

# 三层装饰器


def log2(text):
    def warpper(func):
        def inner(*args, **kw):
            print(text, func.__name__)
            return func(*args, **kw)
        return inner
    return warpper


@log2('测试文本')
def de2():
    print("hehe")


de2()
# 装饰器函数使用后函数名会变成装饰器的函数名
print(de.__name__)
print(de2.__name__)
# 函数名


def log3(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s():' % (text + "前", func.__name__))
            func(*args, **kw)
            print('%s %s():' % (text + "后", func.__name__))
        return wrapper
    return decorator


@log3('测试文本')
def de3():
    print("hehe")


de3()
print(de3.__name__)


# 练习 既支持@log又支持@log()   以下代码没太大意义


def log4(param):
    remark = "默认输出"
    # if isinstance(param, (str, int ,float, bool, list, dict, tuple)):
    if type(param) != types.FunctionType:
        remark = str(param)

        def decorator(func):
            @functools.wraps(func)
            def wrapper(*args, **kw):
                print('%s %s():' % (remark + "前", func.__name__))
                func(*args, **kw)
                print('%s %s():' % (remark + "后", func.__name__))
            return wrapper
        return decorator
    else:
        def warpper(*args, **kw):
            print(remark, param.__name__)
            return param(*args, **kw)
        return warpper


@log4
def de4():
    print("hehe")


de4()


# 偏函数 可以固定住函数的某些参数
int2 = functools.partial(int, base=2)
max2 = functools.partial(max, 50)
print("int2", int2("10010"))
print("max2", max2(10, 20, 30))
